// Author: Domien Nowicki


#include "Map.h"
#include <assert.h>
#include <algorithm>

Map::Map(): rowCount(0), columnCount(0), cellmap(0)
{
	// Set a default map size
	DefaultResize();
}

Map::Map(const Map& rhs): cellmap(0), rowCount(rhs.rowCount), columnCount(rhs.columnCount)
{
	cellmap = new Cell*[columnCount * rowCount];

	CopyCells(cellmap, rhs.cellmap, columnCount, rowCount, rhs.columnCount, rhs.rowCount);
}

Map::~Map()
{
	Clear();
}

void Map::Clear()
{

	// Clear all used resources
	if (!cellmap)
	{
		return;
	}
	

	// Delete each cell
	for(unsigned int row=0; row<rowCount; row++)
	{
		for(unsigned int column=0; column<columnCount; column++)
		{
			unsigned int index = row*columnCount+column;

			if (cellmap[index])
			{
				delete cellmap[index];
			}
		}
	}

	// And delete the array
	delete[] cellmap;

	cellmap = 0;
}

// Copy original cells over to the new map
void Map::CopyCells(Cell** dstCellmap, Cell** srcCellmap, unsigned int newColumnCount, unsigned int newRowCount, unsigned int oldColumnCount, unsigned int oldRowCount)
{
	// Check how many cells we should copy from the original
	unsigned int copyRowCount = std::min(oldRowCount, newRowCount);
	unsigned int copyColumnCount = std::min(oldColumnCount, newColumnCount);


	// Nothing to copy if no cellmap present
	if (0 == srcCellmap)
		return;

	for(unsigned int row=0; row<copyRowCount; row++)
	{
		for(unsigned int column=0; column<copyColumnCount; column++)
		{
			unsigned int oldindex = row * oldColumnCount + column;
			unsigned int newindex = row * newColumnCount + column;

			// Copy cells
			dstCellmap[newindex] = NewCell(srcCellmap[oldindex], column, row);
		}
	}
}

void Map::InitCells(Cell** newcellmap, unsigned int newColumnCount, unsigned int newRowCount)
{
	for(unsigned int row=0; row<newRowCount; row++)
	{
		for(unsigned int column=0; column<newColumnCount; column++)
		{
			unsigned int index = row*newColumnCount+column;
			// Create new cells
			if (!newcellmap[index])
			{
				newcellmap[index] = NewCell(0, column, row);
			}
		}
	}
}

void Map::ClearCells(Cell** newcellmap, unsigned int newColumnCount, unsigned int newRowCount)
{
	for(unsigned int row=0; row<newRowCount; row++)
	{
		for(unsigned int column=0; column<newColumnCount; column++)
		{
			// Initialize cells
			newcellmap[row*newColumnCount+column] = 0;
		}
	}
}

Cell* Map::NewCell(const Cell* copy, unsigned int column, unsigned int row)
{
	if (copy)
	{
		return new Cell(*copy);
	}
	else
	{
		return new Cell(column, row);
	}
}

void Map::DefaultResize()
{
	Resize(DEFAULT_ROWCOUNT, DEFAULT_COLUMNCOUNT);
}

void Map::Resize(unsigned int newColumnCount, unsigned int newRowCount)
{
	// Create a new cellmap, which will later become the real cellmap after copying
	Cell** newcellmap = new Cell*[newRowCount * newColumnCount];

	ClearCells(newcellmap, newColumnCount, newRowCount);

	// Copy cells from the original map to the new map
	CopyCells(newcellmap, cellmap, newColumnCount, newRowCount, columnCount, rowCount);

	InitCells(newcellmap, newColumnCount, newRowCount);


	// Cleanup the old cellmap
	Clear();

	// And use new one
	cellmap = newcellmap;

	rowCount = newRowCount;
	columnCount = newColumnCount;
}



// Information about the map dimensions
unsigned int Map::GetRowCount() const
{
	return rowCount;
}

unsigned int Map::GetColumnCount() const
{
	return columnCount;
}

// Calculate index based on row and column
unsigned int Map::GetIndex(unsigned int column, unsigned int row) const
{
	assert(0 != cellmap && column < GetColumnCount() && row < GetRowCount());

	return row*GetColumnCount()+column;
}


Cell& Map::GetCell(unsigned int column, unsigned int row)
{
	assert(0 != cellmap);

	Cell *cell = cellmap[GetIndex(column, row)];

	assert(0 != cell);

	return *cell;
}

const Cell& Map::GetCell(unsigned int column, unsigned int row) const
{
	assert(0 != cellmap);

	const Cell *cell = cellmap[GetIndex(column, row)];

	assert(0 != cell);

	return *cell;
}


Cell& Map::operator()(unsigned int column, unsigned int row)
{
	return GetCell(column, row);
}

const Cell& Map::operator()(unsigned int column, unsigned int row) const
{
	return GetCell(column, row);
}


// Easy-access functions for a specific cell inside the map
bool Map::IsEmptyCell(unsigned int column, unsigned int row) const
{
	return GetCell(column, row).IsEmpty();
}

void Map::SetWallCell(unsigned int column, unsigned int row)
{
	GetCell(column, row).SetWall();
}

void Map::SetEmptyCell(unsigned int column, unsigned int row)
{
	GetCell(column, row).SetEmpty();
}

void Map::SetTextureCell(unsigned int column, unsigned int row, Cell::Side side, unsigned int tindex, const Pixel& userColor)
{
	GetCell(column, row).SetTexture(side, tindex, userColor);
}

unsigned int Map::GetTextureCell(unsigned int column, unsigned int row, Cell::Side side) const
{
	return GetCell(column, row).GetTexture(side);
}
